﻿using System;

namespace PT.NoteDetection.Midi {
    public class TimeService {
        // NAudio Midi export currently supports only MIDI timing in 'Ticks per beat' mode, 
        // To calculate absolute time in those terms, the Tempo and Ticks Per Quarter Note 
        // must be known.

        /// <summary>MIDI ticks per quarter note.</summary>
        /// <remarks>480 is the value used by Apple's Logic.</remarks>
        public const int TicksPerQuarterNote = 480;
        const int DefaultTempo = 90;

        public int Tempo { get; set; }

        /// <summary>TimeSpan ticks per second.</summary>
        /// <remarks>
        /// 1 Tick = 100 * 1E-09 (s) = 1E-07 (s)
        /// 1 Second = 1E+7 Ticks = 10 000 000
        /// </remarks>
        public const long TicksPerSecond = 10000000L;

        public TimeService() {
            Tempo = DefaultTempo;
        }

        /// <summary>Calculates the relative time in MIDI units.</summary>
        /// <param name="time">Time offset from the start event.</param>
        /// <returns>Absolute note time in MIDI units.</returns>
        public long CalculateMidiTime(TimeSpan time) {
            return CalculateMidiTime(time.Ticks);
        }

        /// <summary>Calculates the relative time in MIDI units.</summary>
        /// <param name="timeTicks">Time offset from the start event in ticks.</param>
        /// <returns>Absolute note time in MIDI units.</returns>
        public long CalculateMidiTime(long timeTicks) {
            // midiTicks = midiTicks/second (mtps) * seconds
            // midiTicks = midiTicks/click (mtcs) * clicks (c)
            // midiTicks =  midiTicks/second (mtps) : clicks/second (cps) * clicks (c)
            // res = mtps / cps * c = mtps * c / cps

            // Calculate MIDI ticks per second (the target unit)
            // Tempo := "Quarter notes" / Minute = Q / 60s
            long midiTicksPerSecond = checked(TicksPerQuarterNote * Tempo / 60);

            // Calculate result
            return midiTicksPerSecond * timeTicks / TicksPerSecond;
        }

        public TimeSpan CalculateTime(long midiTime) {
            long midiTicksPerSecond = checked(TicksPerQuarterNote * Tempo / 60);
            return new TimeSpan(midiTime * TicksPerSecond / midiTicksPerSecond);
        }

        public int Duration16Th { get { return CalculateNoteDuration(1, 16); } }

        public int CalculateNoteDuration(int numerator, int denumerator) {
            return 4 * TicksPerQuarterNote * numerator / denumerator;
        }
    }
}
